package net.tp.util;

import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;

public final class Utils {

    /**
     * Natural comparator for any element type.
     * This is different from {@link java.util.Comparator#naturalOrder} which requires that the element type extends {@link Comparable}.
     * @param <E> element type
     * @return Natural Comparator
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <E> Comparator<? super E> naturalComparator() {
        return cast(Comparator.<Comparable> naturalOrder());
    }

    /**
     * Cast to any type without check.
     *
     * @param <T> new type
     * @param o object
     * @return the object as type T
     */
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object o) {
        return (T)o;
    }

    public static void parallelCopy(int[] src, int srcPost, int[] dest, int destPos, int length) {
        System.arraycopy(src, srcPost, dest, destPos, length);
    }

    /**
     * Execute threads in parallel and wait for them to complete.
     * @param service ExecutorService
     * @param from from index
     * @param to to index
     * @param K number of ranges
     * @param consumer consumer to host implementation for a particular thread index k
     */
    public static void parallelRanges(ExecutorService service, int from, int to, int K, ThreeIntConsumer consumer) {
        int N = to - from;
        int M = 1 + (N-1)/K;
        List<Callable<Object>> workers = new ArrayList<>(K);
        for (int k = 0; k < K; k++) {
            workers.add(new RangeCallable(k, from+k*M, Math.min(from+k*M+M,to), consumer));
        }
        try {
            service.invokeAll(workers);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @FunctionalInterface
    public static interface ThreeIntConsumer {
        public void accept(int a, int b, int c);
    }

    public static class RangeCallable implements Callable<Object> {
        private final int k;
        private final int from, to;
        private final ThreeIntConsumer consumer;
        public RangeCallable(int k, int from, int to, ThreeIntConsumer consumer) {
            this.k = k;
            this.from = from;
            this.to = to;
            this.consumer = consumer;
        }
        @Override
        public Object call() {
            consumer.accept(k, from, to);
            return null;
        }
    }

    public static RecursiveAction makeRecursiveAction(Runnable runnable) {
        return new RecursiveAction() {
            private final static long serialVersionUID = 2;
            @Override
            public void compute() {
                runnable.run();
            }
        };
    }

    /**
     * Convert collection to Int Array.
     * @param integers integer collection
     * @return integer array
     */
    public static int[] toIntArray(Collection<Integer> integers) {
        return toIntArray(integers, integers.size());
    }

    /**
     * Convert iterable to Int Array.
     * @param integers integer iterable
     * @param len array size
     * @return integer array
     */
    public static int[] toIntArray(Iterable<Integer> integers, int len) {

        int[] a = new int[len];
        int i = 0;
        for (int v : integers) {
            a[i++] = v;
        }

        return a;
    }

    public static char[] toCharArray(String s) {
        int n = s.length();
        char[] chars = new char[n];
        for (int i = 0; i < n; i++) chars[i] = s.charAt(i);
        return chars;
    }

    /**
     * Null value.
     * @param <T> value type
     * @param value value
     * @param defaultValue value to use if the original value is null
     * @return value if not null, defaultValue if value is null
     */
    public static <T> T nvl(T value, T defaultValue) {
        return value == null ? defaultValue : value;
    }

    /**
     * Null value.
     * @param value value
     * @return value if not null, empty string if value is null. 
     */
    public static String nvl(String value) {
        return nvl(value, "");
    }


    public static <E> boolean isEqual(Iterator<E> i1, Iterator<E> i2) {
        while (i1.hasNext() && i2.hasNext()) {
            E e1 = i1.next();
            E e2 = i2.next();
            if ((e1 == null && e2 != null) || (e1 != null && !e1.equals(e2))) {
                return false;
            }
        }
        return !i1.hasNext() && !i2.hasNext();
    }
}
